/*
 * Debug.cpp
 *
 *  Created on: Dec 12, 2011
 *      Author: kylintse
 */

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>

#include "Debug.h"
#include "gdata.h"

#define DEBUG_PORT 1064
#define DEBUG_FRAME_HEADLEN 9
#define DEBUG_FRAME_MAXLEN 1010

///////////////////////////////////////////////////////////////////////////////
// DebugThread
DebugThread::DebugThread(Debug* debug,QObject* parent)
	: kthread(parent),m_debug(debug)
{

}

DebugThread::~DebugThread()
{

}

void DebugThread::run()
{
	setStarted(true);

	while(true)
	{
		if(needExit())
			break;

		if(m_debug->processRecv()==-1)
		{
			m_debug->closeClient();
			break;
		}
	}

	setStarted(false);
}

///////////////////////////////////////////////////////////////////////////////
// Debug
Debug::Debug()
{
	m_thread=new DebugThread(this);
	m_clientSockFd=-1;
}

Debug::~Debug()
{
	
}

void Debug::run()
{
	m_listenSockFd=socket(AF_INET,SOCK_STREAM,0);
	if(m_listenSockFd==-1)
	{
		setStarted(false);
		return;
	}

	int on=1;
	if(setsockopt(m_listenSockFd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))==-1)
	{
		setStarted(false);
		close(m_listenSockFd);
		return;
	}

	struct sockaddr_in serverAddr;
	memset(&serverAddr,0,sizeof(serverAddr));
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
	serverAddr.sin_port=htons(DEBUG_PORT);

	if(bind(m_listenSockFd,(struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1)
	{
		qWarning()<<errno;
		setStarted(false);
		::close(m_listenSockFd);
		return;
	}

	if(listen(m_listenSockFd,1)==-1)
	{
		setStarted(false);
		::close(m_listenSockFd);
		return;
	}

	int flags=fcntl(m_listenSockFd,F_GETFL,0);
	fcntl(m_listenSockFd,flags|O_NONBLOCK);

	fd_set readfd;
	struct timeval tv;
	int ret;

	setStarted(true);

	while(true)
	{
		if(needExit())
		{
			setStarted(false);
			break;
		}
		tv.tv_sec=1;
		tv.tv_usec=0;

		FD_ZERO(&readfd);
		FD_SET(m_listenSockFd,&readfd);

		ret=::select(m_listenSockFd+1,&readfd,NULL,NULL,&tv);
		switch(ret)
		{
			case -1:
			{
				setStarted(false);
				::close(m_listenSockFd);
				return;
			}

			case 0:
				break;

			default:
			{
				m_clientSockFd=::accept(m_listenSockFd,(struct sockaddr*)NULL,NULL);
				if(m_clientSockFd==-1)
					break;

				m_data.clear();
				m_curDataLen=0;
				m_lenWant=DEBUG_FRAME_HEADLEN;

				m_thread->start(5000);

				break;
			}
		}
	}

	::close(m_listenSockFd);
	setStarted(false);
	m_thread->exit();
	m_thread->wait();
	delete m_thread;
	m_thread=NULL;
	::close(m_clientSockFd);
}

int Debug::processRecv()
{
	fd_set readfd;
	struct timeval tv;
	int ret;

	while(true)
	{
		tv.tv_sec=1;
		tv.tv_usec=0;

		FD_ZERO(&readfd);
		FD_SET(m_clientSockFd,&readfd);

		ret=::select(m_clientSockFd+1,&readfd,NULL,NULL,&tv);

		switch(ret)
		{
			case -1:
			case 0:
			{
				m_data.clear();
				m_curDataLen=0;
				m_lenWant=DEBUG_FRAME_HEADLEN;
				return ret;
			}

			default:
			{
				int recvLen;
				int frameLen;
				recvLen=::read(m_clientSockFd,m_recvBuf+m_curDataLen,m_lenWant);
				if(!recvLen)
				{
					m_data.clear();
					m_curDataLen=0;
					m_lenWant=DEBUG_FRAME_HEADLEN;
					return -1;
				}
				m_curDataLen+=recvLen;
				if(m_curDataLen>=DEBUG_FRAME_HEADLEN)
				{
					if(m_recvBuf[0]=='K'&&
						m_recvBuf[1]=='_'&&
						m_recvBuf[2]=='D'&&
						m_recvBuf[3]=='E'&&
						m_recvBuf[4]=='B'&&
						m_recvBuf[5]=='U'&&
						m_recvBuf[6]=='G')
					{
						frameLen=m_recvBuf[7]+m_recvBuf[8]*0x100;
						m_lenWant=frameLen+DEBUG_FRAME_HEADLEN;
						if(m_curDataLen==m_lenWant)
						{
							m_data.append(m_recvBuf+DEBUG_FRAME_HEADLEN,frameLen);
							if(!(frameLen&0x8000))
							{
								processData();
								m_data.clear();
								m_curDataLen=0;
								m_lenWant=DEBUG_FRAME_HEADLEN;
								return 1;
							}
						}
						else
						{
							m_lenWant-=m_curDataLen;
						}
					}
					else
					{
						m_data.clear();
						m_curDataLen=0;
						m_lenWant=DEBUG_FRAME_HEADLEN;
						return 0;
					}
				}
				break;
			}
		}
	}

	return 0;
}

void Debug::processData()
{
	QByteArray dataSend;

	switch(m_data.at(0))
	{
		case dfcParmReadHardware:
		{
			QDataStream ds(&dataSend,QIODevice::WriteOnly);
			ds<<gEthernetParm[0].mac<<gEthernetParm[0].ip<<gEthernetParm[0].netmask
				<<gEthernetParm[1].mac<<gEthernetParm[1].ip<<gEthernetParm[1].netmask
				<<gCan0Bitrate<<gCan1Bitrate
				<<gswitchAddr<<gswitchType
				<<gSerialParm[0].bitrate<<gSerialParm[0].charbits
				<<gSerialParm[0].stopbits<<gSerialParm[0].parity
				<<gSerialParm[1].bitrate<<gSerialParm[1].charbits
				<<gSerialParm[1].stopbits<<gSerialParm[1].parity
				<<gSerialParm[2].bitrate<<gSerialParm[2].charbits
				<<gSerialParm[2].stopbits<<gSerialParm[2].parity
				<<gSerialParm[3].bitrate<<gSerialParm[3].charbits
				<<gSerialParm[3].stopbits<<gSerialParm[3].parity;
			sendData(dfcParmReadHardware,dataSend);
			break;
		}

		case dfcParmReadDevice:
		{
			QDataStream ds(&dataSend,QIODevice::WriteOnly);
			ds<<gMprDevice;
			ds<<gDeviceListParam.size();
			for(int i=0;i<gDeviceListParam.size();i++)
			{
				ds<<gDeviceListParam[i]->id
					<<gDeviceListParam[i]->type
					<<gDeviceListParam[i]->lstAi
					<<gDeviceListParam[i]->lstDi
					<<gDeviceListParam[i]->lstPi
					<<gDeviceListParam[i]->lstDo
					<<gDeviceListParam[i]->lstAo
					<<gDeviceListParam[i]->lstAction
					<<gDeviceListParam[i]->lstAlarm;
			}
			sendData(dfcParmReadDevice,dataSend);
			break;
		}

		case dfcParmReadHy103:
		{
			QDataStream ds(&dataSend,QIODevice::WriteOnly);
			ds<<gMprDevice;

			ds<<gHy103Cpu.size();
			QMap<quint32,ParamHy103Cpu*>::const_iterator i=gHy103Cpu.constBegin();
			ParamHy103Cpu* hy103Cpu;
			while(i!=gHy103Cpu.constEnd())
			{
				if(gMprDevice)
				{
					if(i.key()<32)
					{
						i++;
						continue;
					}
				}
				hy103Cpu=i.value();
				ds<<i.key();

				ds<<hy103Cpu->devId
					<<hy103Cpu->needAnalysis
					<<hy103Cpu->lstAi
					<<hy103Cpu->lstDi
					<<hy103Cpu->lstPi
					<<hy103Cpu->lstDo
					<<hy103Cpu->lstAo
					<<hy103Cpu->lstAction
					<<hy103Cpu->lstAlarm;
				i++;
			}
			sendData(dfcParmReadHy103,dataSend);
			break;
		}

		case dfcParmReadDevProtocol:
		{
			//sendConfirm(1);
			QDataStream ds(&dataSend,QIODevice::WriteOnly);

			ds<<gDeviceProtocolParam.size();

			for(int i=0;i<gDeviceProtocolParam.size();i++)
			{
				ds<<gDeviceProtocolParam[i]->id
						<<gDeviceProtocolParam[i]->devNode
						<<gDeviceProtocolParam[i]->type;
			}
			sendData(dfcParmReadDevProtocol,dataSend);
		}
			break;

		case dfcParmWriteHardware:
		{
			QDataStream ds(m_data.mid(1));
			ds>>gEthernetParm[0].mac>>gEthernetParm[0].ip>>gEthernetParm[0].netmask
				>>gEthernetParm[1].mac>>gEthernetParm[1].ip>>gEthernetParm[1].netmask
				>>gCan0Bitrate>>gCan1Bitrate
				>>gswitchAddr>>gswitchType
				>>gSerialParm[0].bitrate>>gSerialParm[0].charbits
				>>gSerialParm[0].stopbits>>gSerialParm[0].parity
				>>gSerialParm[1].bitrate>>gSerialParm[1].charbits
				>>gSerialParm[1].stopbits>>gSerialParm[1].parity
				>>gSerialParm[2].bitrate>>gSerialParm[2].charbits
				>>gSerialParm[2].stopbits>>gSerialParm[2].parity
				>>gSerialParm[3].bitrate>>gSerialParm[3].charbits
				>>gSerialParm[3].stopbits>>gSerialParm[3].parity;
			if(saveParam())
				sendConfirm(1);
			else
				sendConfirm(0);
			break;
		}

		case dfcParmWriteDevice:
		{
			QDataStream ds(m_data.mid(1));

			ds>>gMprDevice;
			ParamDevice* device;
			qint32 deviceCount;
			ds>>deviceCount;
			qDeleteAll(gDeviceListParam);
			gDeviceListParam.clear();
			for(int i=0;i<deviceCount;i++)
			{
				device=new ParamDevice;
				ds>>device->id
				    >>device->type
					>>device->lstAi
					>>device->lstDi
					>>device->lstPi
					>>device->lstDo
					>>device->lstAo
					>>device->lstAction
					>>device->lstAlarm;
				gDeviceListParam.append(device);
			}
			if(saveParam())
				sendConfirm(1);
			else
				sendConfirm(0);
			break;
		}

		case dfcParmWriteHy103:
		{
			QDataStream ds(m_data.mid(1));
			ds>>gMprDevice;
			ParamHy103Cpu* cpu;
			qint32 cpuCount;
			quint32 cpuId;
			ds>>cpuCount;
			qDeleteAll(gHy103Cpu);
			gHy103Cpu.clear();
			for(int i=0;i<cpuCount;i++)
			{
				cpu=new ParamHy103Cpu;
				ds>>cpuId;
				gHy103Cpu.insert(cpuId,cpu);
				ds>>cpu->devId
				  >>cpu->needAnalysis
				  >>cpu->lstAi
				  >>cpu->lstDi
				  >>cpu->lstPi
				  >>cpu->lstDo
				  >>cpu->lstAo
				  >>cpu->lstAction
				  >>cpu->lstAlarm;
			}
			if(saveParam())
				sendConfirm(1);
			else
				sendConfirm(0);
			break;
		}

		case dfcParmWriteDevProtocol:
		{
			QDataStream ds(m_data.mid(1));

			qint32 count;
			ParamDeviceProtocol* protocol;

			qDeleteAll(gDeviceProtocolParam);
			gDeviceProtocolParam.clear();
			ds>>count;
			for(int i=0;i<count;i++)
			{
				protocol=new ParamDeviceProtocol;
				ds>>protocol->id
				  >>protocol->devNode
				  >>protocol->type;

				gDeviceProtocolParam.append(protocol);
			}

			if(saveParam())
				sendConfirm(1);
			else
				sendConfirm(0);
			break;

			break;
		}

		case dfcSectorList:
		{
			QDataStream ds(&dataSend,QIODevice::WriteOnly);
			ds<<gActiveSector<<sectorList();
			sendData(dfcSectorList,dataSend);
			break;
		}

		case dfcChangeSector:
		{
			QDataStream ds(m_data.mid(1));
			QStringList lstSector=sectorList();
			QString activeSector;
			ds>>activeSector;
			gActiveSector=activeSector;
			if(!lstSector.contains(activeSector))
			{
				if(!saveParam())
					sendConfirm(0);
			}
			QSettings sectorSetting(gWorkDir+"/sector.ini",QSettings::IniFormat);
			sectorSetting.beginGroup("Sector");
			sectorSetting.setValue("Active",gActiveSector);
			sectorSetting.endGroup();
			sendConfirm(1);
			break;
		}

		default:
			sendConfirm(0);
			break;
	}
}

void Debug::sendData(dgtFuncCode code,QByteArray data)
{
	data.prepend(code);
	qint32 dataLeft=data.size();
	const char* pdata=data.data();
	char sendBuf[1024];

	sendBuf[0]='K';
	sendBuf[1]='_';
	sendBuf[2]='D';
	sendBuf[3]='E';
	sendBuf[4]='B';
	sendBuf[5]='U';
	sendBuf[6]='G';

	while(dataLeft)
	{
		if(dataLeft>DEBUG_FRAME_MAXLEN)
		{
			sendBuf[7]=DEBUG_FRAME_MAXLEN&0xFF;
			sendBuf[8]=(0x80|((DEBUG_FRAME_MAXLEN&0xFF00)>>8));
			memcpy(sendBuf+DEBUG_FRAME_HEADLEN,pdata,DEBUG_FRAME_MAXLEN);
			::write(m_clientSockFd,sendBuf,DEBUG_FRAME_MAXLEN+9);
			pdata+=DEBUG_FRAME_MAXLEN;
			dataLeft-=DEBUG_FRAME_MAXLEN;
		}
		else
		{
			sendBuf[7]=dataLeft&0xFF;
			sendBuf[8]=((dataLeft&0xFF00)>>8);
			memcpy(sendBuf+DEBUG_FRAME_HEADLEN,pdata,dataLeft);
			::write(m_clientSockFd,sendBuf,dataLeft+9);
			break;
		}
	}
}

void Debug::sendConfirm(qint8 confirm)
{
	QByteArray dataSend;
	/*QDataStream ds(&dataSend,QIODevice::WriteOnly);
	ds<<(qint8)dfcConfirm;
	ds<<confirm;*/
	dataSend.append(confirm);
	sendData(dfcConfirm,dataSend);
}

void Debug::closeClient()
{
	if(m_clientSockFd==-1)
		return;

	::close(m_clientSockFd);
	m_clientSockFd=-1;
	m_data.clear();
	m_curDataLen=0;
	m_lenWant=DEBUG_FRAME_HEADLEN;
}
